package com.weisy.signin.network.server;

import android.util.Log;

import com.weisy.signin.network.WebConfig;
import com.weisy.signin.network.resource.IResourceUriHandler;
import com.weisy.signin.network.utils.HttpContext;
import com.weisy.signin.network.utils.StreamToolkit;
import com.weisy.signin.utils.Logs;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author weisy
 * @function
 * @date 2019/3/22 20:39
 */
public class HttpServer {
    private String TAG = "HttpServer";
    private WebConfig webConfig;

    private boolean isEnable = true;
    private ServerSocket socket = null;
    private ExecutorService threadPool;
    private HashSet<IResourceUriHandler> resourceHandlers;

    public HttpServer(WebConfig webConfig) {
        this.webConfig = webConfig;
        threadPool = Executors.newCachedThreadPool();
        resourceHandlers = new HashSet<>();
    }

    /**
     * 启动服务器
     */
    public void startASync() {
        Log.i(TAG, "startASync");
        isEnable = true;
        new Thread(this::doProcSync).start();
    }

    /**
     * 停止服务器
     */
    public void stopASync() {
        if (!isEnable) {
            return;
        }
        isEnable = false;
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean isEnable() {
        return isEnable;
    }

    public void doProcSync() {
        try {
            InetSocketAddress intSocketAddress = new InetSocketAddress(webConfig.getPort());
            socket = new ServerSocket();
            socket.bind(intSocketAddress);
            Log.d(TAG, "doProcSync");
            while (isEnable) {
                Socket remotePeer = socket.accept();
                threadPool.submit(() -> {
                    Log.d(TAG, "remote address=" + remotePeer.getRemoteSocketAddress().toString());
                    onAcceptRemotePeer(remotePeer);
                });
            }
        } catch (IOException e) {
            Log.e(TAG, "doProcSync=" + e.toString());
            e.printStackTrace();
        }
    }

    public void registerResourceHandler(IResourceUriHandler hander) {
        resourceHandlers.add(hander);
    }

    private void onAcceptRemotePeer(Socket remotePeer) {
        try {
            HttpContext httpContext = new HttpContext();
            httpContext.underlySocket = remotePeer;
            InputStream nis = remotePeer.getInputStream();
            String headLine;
            String requestUri = StreamToolkit.readLine(nis).split(" ")[1];
            Log.d(TAG, "requestUri=" + requestUri);
            while ((headLine = StreamToolkit.readLine(nis)) != null) {
                if (headLine.equals("\r\n")) {
                    Log.d(TAG, "header line finish");
                    break;
                }
                Logs.d(TAG, "header=" + headLine);
                String[] str = headLine.split(": ");
                if (str.length > 1) {
                    httpContext.addRequestHeader(str[0], str[1]);
                }
            }
            for (IResourceUriHandler handler : resourceHandlers) {
                if (!handler.accept(requestUri)) {
                    continue;
                }
                handler.handle(requestUri, httpContext);
            }
        } catch (IOException e) {
            Log.e(TAG, "onAcceptRemotePeer:" + e.toString());
        } finally {
            try {
                remotePeer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
